// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ILLink.RoslynAnalyzer.TrimAnalysis;
using ILLink.RoslynAnalyzer.DataFlow;
using ILLink.Shared;
using ILLink.Shared.TrimAnalysis;
using ILLink.Shared.TypeSystemProxy;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace ILLink.RoslynAnalyzer
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public sealed class DynamicallyAccessedMembersAnalyzer : DiagnosticAnalyzer
    {
        internal const string DynamicallyAccessedMembers = nameof(DynamicallyAccessedMembers);
        internal const string DynamicallyAccessedMembersAttribute = nameof(DynamicallyAccessedMembersAttribute);
        public const string attributeArgument = "attributeArgument";
        public const string FullyQualifiedDynamicallyAccessedMembersAttribute = "System.Diagnostics.CodeAnalysis." + DynamicallyAccessedMembersAttribute;
        public const string FullyQualifiedFeatureGuardAttribute = "System.Diagnostics.CodeAnalysis.FeatureGuardAttribute";
        public static Lazy<ImmutableArray<RequiresAnalyzerBase>> RequiresAnalyzers { get; } = new Lazy<ImmutableArray<RequiresAnalyzerBase>>(GetRequiresAnalyzers);
        private static ImmutableArray<RequiresAnalyzerBase> GetRequiresAnalyzers() =>
            ImmutableArray.Create<RequiresAnalyzerBase>(
                new RequiresAssemblyFilesAnalyzer(),
                new RequiresUnreferencedCodeAnalyzer(),
                new RequiresDynamicCodeAnalyzer());

        public static ImmutableArray<DiagnosticDescriptor> GetSupportedDiagnostics()
        {
            var diagDescriptorsArrayBuilder = ImmutableArray.CreateBuilder<DiagnosticDescriptor>(27);
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.RequiresUnreferencedCode));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnExtensionProperties));
            AddRange(DiagnosticId.MethodParameterCannotBeStaticallyDetermined, DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter);
            AddRange(DiagnosticId.DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings, DiagnosticId.DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings);
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersFieldAccessedViaReflection));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberWithRequiresUnreferencedCode));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithRequiresUnreferencedCode));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberWithDynamicallyAccessedMembers));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithDynamicallyAccessedMembers));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.UnrecognizedTypeInRuntimeHelpersRunClassConstructor));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMismatchOnGenericParameterBetweenOverrides));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMismatchOnImplicitThisBetweenOverrides));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.PropertyAccessorParameterInLinqExpressionsCannotBeStaticallyDetermined));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.MakeGenericType));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.MakeGenericMethod));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.CaseInsensitiveTypeGetTypeCallIsNotSupported));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.UnrecognizedTypeNameInTypeGetType));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.UnrecognizedParameterInMethodCreateInstance));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.ParametersOfAssemblyCreateInstanceCannotBeAnalyzed));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.TypeNameIsNotAssemblyQualified));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.ReturnValueDoesNotMatchFeatureGuards));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.InvalidFeatureGuard));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined));
            diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DataflowAnalysisDidNotConverge));

            foreach (var requiresAnalyzer in RequiresAnalyzers.Value)
            {
                foreach (var diagnosticDescriptor in requiresAnalyzer.SupportedDiagnostics)
                    diagDescriptorsArrayBuilder.Add(diagnosticDescriptor);
            }

            return diagDescriptorsArrayBuilder.ToImmutable();

            void AddRange(DiagnosticId first, DiagnosticId last)
            {
                Debug.Assert((int)first < (int)last);

                for (int i = (int)first;
                    i <= (int)last; i++)
                {
                    diagDescriptorsArrayBuilder.Add(DiagnosticDescriptors.GetDiagnosticDescriptor((DiagnosticId)i));
                }
            }
        }

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => GetSupportedDiagnostics();

        internal static Location GetPrimaryLocation(ImmutableArray<Location>? locations)
        {
            if (locations is null)
                return Location.None;

            return locations.Value.Length > 0 ? locations.Value[0] : Location.None;
        }

        public override void Initialize(AnalysisContext context)
        {
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);

            if (!Debugger.IsAttached)
                context.EnableConcurrentExecution();

            context.RegisterCompilationStartAction(context =>
            {
                var dataFlowAnalyzerContext = DataFlowAnalyzerContext.Create(context.Options, context.Compilation, RequiresAnalyzers.Value);
                if (!dataFlowAnalyzerContext.AnyAnalyzersEnabled)
                    return;

                context.RegisterOperationBlockAction(context =>
                {
                    foreach (var operationBlock in context.OperationBlocks)
                    {
                        TrimDataFlowAnalysis trimDataFlowAnalysis = new(context, dataFlowAnalyzerContext, operationBlock);
                        bool success = trimDataFlowAnalysis.InterproceduralAnalyze();
                        trimDataFlowAnalysis.ReportDiagnostics(context.ReportDiagnostic);
                        if (!success)
                        {
                            context.ReportDiagnostic(
                                Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(
                                    DiagnosticId.DataflowAnalysisDidNotConverge,
                                    diagnosticSeverity: DiagnosticSeverity.Warning),
                                operationBlock.Syntax.GetLocation(),
                                operationBlock.FindContainingSymbol(context.OwningSymbol).GetDisplayName()));
                        }
                    }
                });

                // Remaining actions are only for DynamicallyAccessedMembers analysis.
                if (!dataFlowAnalyzerContext.EnableTrimAnalyzer)
                    return;

                // Examine generic instantiations in base types and interface list
                context.RegisterSymbolAction(context =>
                {
                    var type = (INamedTypeSymbol)context.Symbol;
                    // RUC on type doesn't silence DAM warnings about generic base/interface types.
                    // This knowledge lives in IsInRequiresUnreferencedCodeAttributeScope,
                    // which we still call for consistency here, but it is expected to return false.
                    if (type.IsInRequiresUnreferencedCodeAttributeScope(out _))
                        return;

                    var location = GetPrimaryLocation(type.Locations);

                    var typeNameResolver = new TypeNameResolver(context.Compilation);
                    var genericArgumentDataFlow = new GenericArgumentDataFlow(dataFlowAnalyzerContext, FeatureContext.None, typeNameResolver, type, location, context.ReportDiagnostic);
                    if (type.BaseType is INamedTypeSymbol baseType)
                        genericArgumentDataFlow.ProcessGenericArgumentDataFlow(baseType);

                    foreach (var interfaceType in type.Interfaces)
                        genericArgumentDataFlow.ProcessGenericArgumentDataFlow(interfaceType);

                    DynamicallyAccessedMembersTypeHierarchy.ApplyDynamicallyAccessedMembersToTypeHierarchy(typeNameResolver, location, type, context.ReportDiagnostic);
                }, SymbolKind.NamedType);
                context.RegisterSymbolAction(context =>
                {
                    VerifyMemberOnlyApplyToTypesOrStrings(context, context.Symbol);
                    VerifyDamOnPropertyAndAccessorMatch(context, (IMethodSymbol)context.Symbol);
                    VerifyDamOnDerivedAndBaseMethodsMatch(context, (IMethodSymbol)context.Symbol);
                }, SymbolKind.Method);
                context.RegisterSymbolAction(context =>
                {
                    VerifyDamOnInterfaceAndImplementationMethodsMatch(context, (INamedTypeSymbol)context.Symbol);
                }, SymbolKind.NamedType);
                context.RegisterSymbolAction(context =>
                {
                    VerifyMemberOnlyApplyToTypesOrStrings(context, context.Symbol);
                }, SymbolKind.Property);
                context.RegisterSymbolAction(context =>
                {
                    VerifyMemberOnlyApplyToTypesOrStrings(context, context.Symbol);
                }, SymbolKind.Field);
            });
        }

        private static void VerifyMemberOnlyApplyToTypesOrStrings(SymbolAnalysisContext context, ISymbol member)
        {
            var location = GetPrimaryLocation(member.Locations);
            if (member is IFieldSymbol field && field.GetDynamicallyAccessedMemberTypes() != DynamicallyAccessedMemberTypes.None && !field.Type.IsTypeInterestingForDataflow(isByRef: field.RefKind is not RefKind.None))
                context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings), location, member.GetDisplayName()));
            else if (member is IMethodSymbol method)
            {
                if (method.GetDynamicallyAccessedMemberTypesOnReturnType() != DynamicallyAccessedMemberTypes.None && !method.ReturnType.IsTypeInterestingForDataflow(isByRef: method.ReturnsByRef))
                    context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings), location, member.GetDisplayName()));
                if (method.GetDynamicallyAccessedMemberTypes() != DynamicallyAccessedMemberTypes.None && !method.ContainingType.IsTypeInterestingForDataflow(isByRef: false))
                    context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods), location));
                foreach (var parameter in method.Parameters)
                {
                    if (parameter.GetDynamicallyAccessedMemberTypes() != DynamicallyAccessedMemberTypes.None && !parameter.Type.IsTypeInterestingForDataflow(isByRef: parameter.RefKind is not RefKind.None))
                        context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings), location, parameter.GetDisplayName(), member.GetDisplayName()));
                }
            }
            else if (member is IPropertySymbol property)
            {
                if (property.GetDynamicallyAccessedMemberTypes() != DynamicallyAccessedMemberTypes.None && !property.Type.IsTypeInterestingForDataflow(isByRef: property.ReturnsByRef))
                    context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings), location, member.GetDisplayName()));
                if (property.GetDynamicallyAccessedMemberTypes() != DynamicallyAccessedMemberTypes.None && property.ContainingType.ExtensionParameter != null)
                    context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnExtensionProperties), location, member.GetDisplayName()));
            }
        }

        private static void VerifyDamOnDerivedAndBaseMethodsMatch(SymbolAnalysisContext context, IMethodSymbol methodSymbol)
        {
            if (methodSymbol.TryGetOverriddenMember(out var overriddenSymbol) && overriddenSymbol is IMethodSymbol overriddenMethod
                && context.Symbol is IMethodSymbol method)
            {
                VerifyDamOnMethodsMatch(context, method, overriddenMethod);
            }
        }

        private static void VerifyDamOnMethodsMatch(SymbolAnalysisContext context, IMethodSymbol overrideMethod, IMethodSymbol baseMethod, ISymbol? origin = null)
        {
            var overrideMethodReturnAnnotation = FlowAnnotations.GetMethodReturnValueAnnotation(overrideMethod);
            var baseMethodReturnAnnotation = FlowAnnotations.GetMethodReturnValueAnnotation(baseMethod);
            if (overrideMethodReturnAnnotation != baseMethodReturnAnnotation)
            {

                (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements(overrideMethod,
                    baseMethod, overrideMethodReturnAnnotation, baseMethodReturnAnnotation);

                Location attributableSymbolLocation = GetPrimaryLocation(attributableMethod.Locations);

                // code fix does not support merging multiple attributes. If an attribute is present or the method is not in source, do not provide args for code fix.
                (Location[]? sourceLocation, Dictionary<string, string?>? DAMArgs) = (!attributableSymbolLocation.IsInSource
                    || (overrideMethod.TryGetReturnAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)
                        && baseMethod.TryGetReturnAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _))
                        ) ? (null, null) : CreateArguments(attributableSymbolLocation, missingAttribute);

                var returnOrigin = origin ??= overrideMethod;
                context.ReportDiagnostic(Diagnostic.Create(
                    DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides),
                    GetPrimaryLocation(returnOrigin.Locations), sourceLocation, DAMArgs?.ToImmutableDictionary(), overrideMethod.GetDisplayName(), baseMethod.GetDisplayName()));
            }

            foreach (var overrideParam in overrideMethod.GetMetadataParameters())
            {
                var baseParam = baseMethod.GetParameter(overrideParam.Index);
                var baseParameterAnnotation = FlowAnnotations.GetMethodParameterAnnotation(baseParam);
                var overrideParameterAnnotation = FlowAnnotations.GetMethodParameterAnnotation(overrideParam);
                if (overrideParameterAnnotation != baseParameterAnnotation)
                {
                    (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements(overrideMethod,
                        baseMethod, overrideParameterAnnotation, baseParameterAnnotation);

                    Location attributableSymbolLocation = attributableMethod.GetParameter(overrideParam.Index).Location!;

                    // code fix does not support merging multiple attributes. If an attribute is present or the method is not in source, do not provide args for code fix.
                    (Location[]? sourceLocation, Dictionary<string, string?>? DAMArgs) = (!attributableSymbolLocation.IsInSource
                        || (overrideParam.ParameterSymbol!.TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)
                            && baseParam.ParameterSymbol!.TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _))
                            ) ? (null, null) : CreateArguments(attributableSymbolLocation, missingAttribute);

                    var parameterOrigin = origin ?? overrideParam.ParameterSymbol;
                    context.ReportDiagnostic(Diagnostic.Create(
                        DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides),
                        GetPrimaryLocation(parameterOrigin?.Locations), sourceLocation, DAMArgs?.ToImmutableDictionary(),
                        overrideParam.GetDisplayName(), overrideMethod.GetDisplayName(), baseParam.GetDisplayName(), baseMethod.GetDisplayName()));
                }
            }

            for (int i = 0; i < overrideMethod.TypeParameters.Length; i++)
            {
                var methodTypeParameterAnnotation = overrideMethod.TypeParameters[i].GetDynamicallyAccessedMemberTypes();
                var overriddenMethodTypeParameterAnnotation = baseMethod.TypeParameters[i].GetDynamicallyAccessedMemberTypes();
                if (methodTypeParameterAnnotation != overriddenMethodTypeParameterAnnotation)
                {

                    (IMethodSymbol attributableMethod, DynamicallyAccessedMemberTypes missingAttribute) = GetTargetAndRequirements(overrideMethod, baseMethod, methodTypeParameterAnnotation, overriddenMethodTypeParameterAnnotation);

                    var attributableSymbol = attributableMethod.TypeParameters[i];
                    Location attributableSymbolLocation = GetPrimaryLocation(attributableSymbol.Locations);

                    // code fix does not support merging multiple attributes. If an attribute is present or the method is not in source, do not provide args for code fix.
                    (Location[]? sourceLocation, Dictionary<string, string?>? DAMArgs) = (!attributableSymbolLocation.IsInSource
                        || (overrideMethod.TypeParameters[i].TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)
                            && baseMethod.TypeParameters[i].TryGetAttribute(DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _))
                            ) ? (null, null) : CreateArguments(attributableSymbolLocation, missingAttribute);

                    var typeParameterOrigin = origin ?? overrideMethod.TypeParameters[i];
                    context.ReportDiagnostic(Diagnostic.Create(
                        DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMismatchOnGenericParameterBetweenOverrides),
                        GetPrimaryLocation(typeParameterOrigin.Locations), sourceLocation, DAMArgs?.ToImmutableDictionary(),
                        overrideMethod.TypeParameters[i].GetDisplayName(), overrideMethod.GetDisplayName(),
                        baseMethod.TypeParameters[i].GetDisplayName(), baseMethod.GetDisplayName()));
                }
            }

            if (!overrideMethod.IsStatic)
            {
                var overrideMethodThisAnnotation = FlowAnnotations.GetMethodParameterAnnotation(new ParameterProxy(new(overrideMethod), (ParameterIndex)0));
                var baseMethodThisAnnotation = FlowAnnotations.GetMethodParameterAnnotation(new ParameterProxy(new(baseMethod), (ParameterIndex)0));
                if (overrideMethodThisAnnotation != baseMethodThisAnnotation)
                {
                    var methodOrigin = origin ?? overrideMethod;
                    context.ReportDiagnostic(Diagnostic.Create(
                        DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersMismatchOnImplicitThisBetweenOverrides),
                        GetPrimaryLocation(methodOrigin.Locations),
                        overrideMethod.GetDisplayName(), baseMethod.GetDisplayName()));
                }
            }
        }

        private static void VerifyDamOnInterfaceAndImplementationMethodsMatch(SymbolAnalysisContext context, INamedTypeSymbol type)
        {
            foreach (var (interfaceMember, implementationMember) in type.GetMemberInterfaceImplementationPairs())
            {
                if (implementationMember is IMethodSymbol implementationMethod && interfaceMember is IMethodSymbol interfaceMethod)
                {
                    ISymbol origin = implementationMethod;
                    INamedTypeSymbol implementationType = implementationMethod.ContainingType;

                    // If this type implements an interface method through a base class, the origin of the warning is this type,
                    // not the member on the base class.
                    if (!implementationType.IsInterface() && !SymbolEqualityComparer.Default.Equals(implementationType, type))
                        origin = type;

                    VerifyDamOnMethodsMatch(context, implementationMethod, interfaceMethod, origin);
                }
            }
        }

        private static void VerifyDamOnPropertyAndAccessorMatch(SymbolAnalysisContext context, IMethodSymbol methodSymbol)
        {
            if ((methodSymbol.MethodKind != MethodKind.PropertyGet && methodSymbol.MethodKind != MethodKind.PropertySet)
                || methodSymbol.AssociatedSymbol is not IPropertySymbol propertySymbol
                || !propertySymbol.Type.IsTypeInterestingForDataflow(isByRef: propertySymbol.RefKind is not RefKind.None)
                || propertySymbol.GetDynamicallyAccessedMemberTypes() == DynamicallyAccessedMemberTypes.None)
                return;

            // For C# 14 extension properties, property-level DAM does not propagate to accessors
            // and we do not consider property vs accessor conflicts meaningful. Skip conflict checks.
            if (methodSymbol.HasExtensionParameterOnType())
                return;

            // None on the return type of 'get' matches unannotated
            if (methodSymbol.MethodKind == MethodKind.PropertyGet
                && methodSymbol.GetDynamicallyAccessedMemberTypesOnReturnType() != DynamicallyAccessedMemberTypes.None
                // None on parameter of 'set' matches unannotated
                || methodSymbol.MethodKind == MethodKind.PropertySet
                && methodSymbol.Parameters[methodSymbol.Parameters.Length - 1].GetDynamicallyAccessedMemberTypes() != DynamicallyAccessedMemberTypes.None)
            {
                context.ReportDiagnostic(Diagnostic.Create(
                    DiagnosticDescriptors.GetDiagnosticDescriptor(DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor),
                    GetPrimaryLocation(propertySymbol.Locations),
                    propertySymbol.GetDisplayName(),
                    methodSymbol.GetDisplayName()
                ));
                return;
            }
        }

        private static (IMethodSymbol Method, DynamicallyAccessedMemberTypes Requirements) GetTargetAndRequirements(IMethodSymbol method, IMethodSymbol overriddenMethod, DynamicallyAccessedMemberTypes methodAnnotation, DynamicallyAccessedMemberTypes overriddenMethodAnnotation)
        {
            DynamicallyAccessedMemberTypes mismatchedArgument;
            IMethodSymbol paramNeedsAttributes;
            if (methodAnnotation == DynamicallyAccessedMemberTypes.None)
            {
                mismatchedArgument = overriddenMethodAnnotation;
                paramNeedsAttributes = method;
            }
            else
            {
                mismatchedArgument = methodAnnotation;
                paramNeedsAttributes = overriddenMethod;
            }
            return (paramNeedsAttributes, mismatchedArgument);
        }

        private static (Location[]?, Dictionary<string, string?>?) CreateArguments(Location attributableSymbolLocation, DynamicallyAccessedMemberTypes mismatchedArgument)
        {
            Dictionary<string, string?>? DAMArgument = new();
            Location[]? sourceLocation = new Location[] { attributableSymbolLocation };
            DAMArgument.Add(DynamicallyAccessedMembersAnalyzer.attributeArgument, mismatchedArgument.ToString());
            return (sourceLocation, DAMArgument);
        }
    }
}
